<?php
/**
 * Greendizer PHP Library
 *
 * @category    Greendizer
 * @package     Greendizer_Net
 * @copyright   Copyright (c) 2009-2010, Greendizer S.A - All rights reserved.
 * @license     Greendizer/Licence.txt
 * @version     1.0
 */

/**
 * @see Greendizer_Net_Response
 */
require_once 'Greendizer/Net/Response.php';

/**
 * @see Greendizer_Net_Uri
 */
require_once 'Greendizer/Net/Uri.php';

/**
 * @see Greendizer_Net_HttpMethods
 */
require_once 'Greendizer/Net/HttpMethods.php';

/**
 * @see Greendizer_Net_ContentTypes
 */
require_once 'Greendizer/Net/ContentTypes.php';

/**
 * @see Greendizer_Net_AuthenticationTypes
 */
require_once 'Greendizer/Net/AuthenticationTypes.php';

/**
 * Represents an Http Request
 *
 * @category    Greendizer
 * @package     Greendizer_Net
 * @copyright   Copyright (c) 2009-2010, Greendizer S.A - All rights reserved.
 * @license     Greendizer/License.txt
 */
class Greendizer_Net_Request {

    /**
     * cURL handle
     *
     * @var cURL handle
     */
    private $curlHandle;

    /**
     * Http Method of the request
     *
     * @var string
     */
    private $method;

    /**
     * Uri requested
     *
     * @var Greendizer_Net Uri
     */
    private $url;

    /**
     * The content-type of the request
     *
     * @var string
     */
    private $contentType;

    /**
     * Array of request headers
     *
     * @var array
     */
    private $headers = array();

    /**
     * Request Body
     *
     * @var string
     */
    private $data;

    /**
     * Headers of the Response
     *
     * @var type
     */
    private $responseHeaders = array();


    /**
     * Initializes a new Request
     *
     * @param string $method            HTTP Method
     * @param Greendizer_Net_Uri $uri   Uri Requested
     * @param array $headers            Array of request headers
     * @param string $authType          Authentification type 'basic' or 'OAuth'
     * @param array $credentials        Array of Authentication credentials
     * @param string $data              Request body
     * @param string $contentType       Content-Type header of the request
     */
    public function __construct($method, Greendizer_Net_Uri $uri, $headers, $authType=null, $credentials=null, $data=null, $contentType=Greendizer_Net_ContentTypes::ENCODED_URL) {
        //validate input
        $this->validateInput($method, $uri, $headers, $authType, $credentials, $contentType);

        //Initializing variables
        $this->method = $method;
        $this->url = $uri;
        $this->headers = $headers;
        //In case the Auth method is OAuth, need to add the token
        if ($authType == 'OAuth') {
            $this->headers['Authorization'] = sprintf("Bearer %s", $credentials['token']);
        }
        $this->headers['User-Agent'] = 'PHP Client/1.0';
        $this->data = $data;
        $this->contentType = $contentType.';charset=UTF-8';


        //--Initializing CURL Handle
        $this->curlHandle = curl_init();
        //--CURL Options
        curl_setopt($this->curlHandle, CURLOPT_URL, $this->url->getAbsoluteUri());
        curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, $this->formatHeaders($this->headers));
        curl_setopt($this->curlHandle, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->curlHandle, CURLOPT_HEADER, true);
        //--CURL SSL Options [Need to change on real server]
        //TODO: Add the SSL back when on prod
        curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYPEER, false);

        //In case of basic Auth
        if ($authType == Greendizer_Net_AuthenticationTypes::BASIC) {
            //--CURL Http Basic Auth
            curl_setopt($this->curlHandle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
            curl_setopt($this->curlHandle, CURLOPT_USERPWD, $credentials['email'] . ':' . $credentials['password']);
        }
    }

    /**
     * Sends the request and returns its response
     *
     * @return Greendizer_Net_Response
     */
    public function getResponse() {

        if (isset($this->data)) {
            $this->addHeader('Content-Type', $this->contentType);
        }

        switch (strtoupper($this->method)) {

            case Greendizer_Net_HttpMethods::GET:
                break;

            case Greendizer_Net_HttpMethods::HEAD:
                curl_setopt($this->curlHandle, CURLOPT_CUSTOMREQUEST, Greendizer_Net_HttpMethods::HEAD);
                curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $this->data);
                break;

            case Greendizer_Net_HttpMethods::POST:
                curl_setopt($this->curlHandle, CURLOPT_POST, true);
                curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $this->data);
                break;

            case Greendizer_Net_HttpMethods::OPTIONS:
                curl_setopt($this->curlHandle, CURLOPT_CUSTOMREQUEST, Greendizer_Net_HttpMethods::OPTIONS);
                curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $this->data);
                $this->addHeader("X-HTTP-Method-Override", $this->method);
                break;

            default:
                curl_setopt($this->curlHandle, CURLOPT_POST, true);
                curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $this->data);
                $this->addHeader("X-HTTP-Method-Override", $this->method);
                break;
        }

        $curlresponse = curl_exec($this->curlHandle); //Send Request
        //Deal with the headers and body of the response
        $this->responseHeaders = curl_getinfo($this->curlHandle); //default curl headers
        $someArray = $this->parseHttpResponse($curlresponse);
        $this->responseHeaders = array_merge($this->responseHeaders, $someArray[0]);
        $data = $someArray[1];
        //Create the response Object
        $response = new Greendizer_Net_Response($this, $this->responseHeaders, $data);
        curl_close($this->curlHandle); //Close Request
        return $response;
    }

    /**
     * Adds a header to the request
     *
     * @param string $header
     * @param string $value
     */
    private function addHeader($header, $value) {
        $this->headers[$header] = $value;
        curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, $this->formatHeaders($this->headers));
    }

    /**
     * Formats the header array for the cURL handle
     *
     * @param array $headers
     * @return array
     */
    private function formatHeaders($headers) {
        $i = 0;
        $array = array();
        foreach ($headers as $head => $value) {
            array_push($array, $head . ': ' . $value);
        }
        return $array;
    }

    /**
     * Parses the Http response and extracts its headers and body
     *
     * @param string $responseString
     * @return array
     */
    private function parseHttpResponse($responseString) {

        $headers = array();
        $content = '';
        $str = strtok($responseString, "\n");
        $h = null;
        while ($str !== false) {
            if ($h and trim($str) === '') {
                $h = false;
                continue;
            }
            if ($h !== false and false !== strpos($str, ':')) {
                $h = true;
                list($headername, $headervalue) = explode(':', trim($str), 2);
                $headername = strtolower($headername);
                $headervalue = ltrim($headervalue);
                if (isset($headers[$headername]))
                    $headers[$headername] .= ',' . $headervalue;
                else
                    $headers[$headername] = $headervalue;
            }
            if ($h === false) {
                $content .= $str . "\n";
            }
            $str = strtok("\n");
        }
        return array($headers, trim($content));
    }

    /**
     * Validates the input of the request contructor
     *
     * @param string $method            HTTP Method
     * @param Greendizer_Net_Uri $uri   Uri Requested
     * @param array $headers            Array of request headers
     * @param string $authType          Authentification type 'basic' or 'OAuth'
     * @param array $credentials        Array of Authentication credentials
     * @param string $contentType       Content-Type header of the request
     */
    private function validateInput($method, $uri, $headers, $authType, $credentials, $contentType) {
        if (!in_array($method, Greendizer_Net_HttpMethods::$METHODS)) {
            throw new InvalidArgumentException('Invalid Argument 1 for Greendizer_Net_Request Constructor: Invalid Http Method');
        }
        if (get_class($uri) != 'Greendizer_Net_Uri') {
            throw new InvalidArgumentException('Invalid Argument 2 for Greendizer_Net_Request Constructor: Expects object Greendizer_Net_Uri');
        }
        if (!is_array($headers) && $headers != null) {
            throw new InvalidArgumentException('Invalid Argument 3 for Greendizer_Net_Request Constructor: Expects an Array');
        }
        if (!in_array($authType, Greendizer_Net_AuthenticationTypes::$TYPES)) {
            throw new InvalidArgumentException('Invalid Argument 4 for Greendizer_Net_Request Constructor: Invalid Authentication Type');
        }
        if ($credentials != null) {
            if (!is_array($credentials) && $credentials != null) {
                throw new InvalidArgumentException('Invalid Argument 5 for Greendizer_Net_Request Constructor: Expects an Array');
            }
            if ($authType == Greendizer_Net_AuthenticationTypes::BASIC && !key_exists('email', $credentials)) {
                throw new InvalidArgumentException('Invalid Argument 5 for Greendizer_Net_Request Constructor: Missing key \'email\' in Array Argument');
            }
            if ($authType == Greendizer_Net_AuthenticationTypes::BASIC && !key_exists('password', $credentials)) {
                throw new InvalidArgumentException('Invalid Argument 5 for Greendizer_Net_Request Constructor: Missing key \'password\' in Array Argument');
            }
            if ($authType == Greendizer_Net_AuthenticationTypes::OAUTH && !key_exists('token', $credentials)) {
                throw new InvalidArgumentException('Invalid Argument 5 for Greendizer_Net_Request Constructor: Missing key \'token\' in Array Argument');
            }
        }
        if (!in_array($contentType, Greendizer_Net_ContentTypes::$TYPES) && $contentType != null) {
            throw new InvalidArgumentException('Invalid Argument 7 for Greendizer_Net_Request Constructor: Invalid Content-type');
        }
    }

}

?>
